import Big from 'big.js'
import { Processor, CalculateOperatorType } from "./processor";

type CalculateNumberType = '0'| '1'| '2'| '3'| '4'| '5'| '6'| '7'| '8'| '9' | '00' | '.' | 'c';

export type ReduceStateModel = {
  userInput: string;
  strNumber?: string;
  strOperator?: CalculateOperatorType;
  processor: Processor;
};

export type DispatchType = CalculateOperatorType | CalculateNumberType | 'c' | '!';
type DispatchActionType = {
  type: DispatchType;
}

const updateWithOperator = (state: ReduceStateModel, input: CalculateOperatorType) => {
  if (!!state.strNumber) {
    state.processor._rawStack.push({
      value: Big(state.strNumber).toNumber(),
    });
    state.strNumber = undefined;
  }
  if (!!state.strOperator) {
    state.strOperator = input;
    state.userInput = state.userInput.slice(0, -1);
  }
  return {
    ...state,
    strOperator: input,
    userInput: state.userInput + input,
  };
}

const updateWithNumber = (state: ReduceStateModel, input: CalculateNumberType) => {
  const newNumber = `${state.strNumber || ''}${input}`;
  try { Big(newNumber) } catch (e) { return state; }
  
  if (!!state.strOperator) {
    state.processor._rawStack.push({
      func: state.strOperator,
    });
    state.strOperator = undefined;
  }

  const strNumber = (input === '.') ? newNumber : Big(newNumber).toNumber().toString();
  const isZero = Big(strNumber).toNumber() === 0;
  const userInput = (isZero && input !=='.') ? '0' : state.userInput + input;
  return {
    ...state,
    strNumber,
    userInput,
  }
}

export const initState = (value?: number): ReduceStateModel => ({
  userInput: value?.toString() || '',
  strNumber: value?.toString() || undefined,
  strOperator: undefined,
  processor: new Processor(),
})

type FnReduceHandlerType = (state: ReduceStateModel, action: DispatchActionType) => ReduceStateModel;
export const reduceHandler: FnReduceHandlerType = (state, action) => {
  if (action.type === 'c') {
      return initState(state.processor._rsltStack[0]?.value);
  }
  if (action.type === '!') {
    state.processor.reduce1Step();
    if (state.processor.status === 'done') {
      // return initState(state.processor._rsltStack[0]?.value);
    }
    return { ...state, strOperator: undefined }
  }
  if (['*', '/', '+', '-', '='].includes(action.type)) {
    return updateWithOperator(state, action.type as CalculateOperatorType);
  }
  if (['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '00', '.'].includes(action.type)) {
    return updateWithNumber(state, action.type as CalculateNumberType);
  }
  return state;
};
